| File: Utilities\ReferenceHolder`1.cs | Web Access |
| Project: src\roslyn\src\Workspaces\Core\Portable\Microsoft.CodeAnalysis.Workspaces.csproj (Microsoft.CodeAnalysis.Workspaces) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; namespace Roslyn.Utilities; internal readonly struct ReferenceHolder<T> : IEquatable<ReferenceHolder<T>> where T : class? { private readonly T? _strongReference; private readonly WeakReference<T>? _weakReference; private readonly int _hashCode; private ReferenceHolder(T strongReference) { _strongReference = strongReference; _weakReference = null; _hashCode = 0; } private ReferenceHolder(WeakReference<T> weakReference, int hashCode) { _strongReference = null; _weakReference = weakReference; _hashCode = hashCode; } public static ReferenceHolder<T> Strong(T value) => new(value); public static ReferenceHolder<T> Weak(T value) { if (value is null) { // Track this as a strong reference so we know 'Equals' should only look at values originally null. return Strong(value); } return new ReferenceHolder<T>(new WeakReference<T>(value), ReferenceEqualityComparer.Instance.GetHashCode(value)); } public T? TryGetTarget() { if (_weakReference is object) return _weakReference.GetTarget(); return _strongReference; } public override bool Equals(object? obj) { return obj is ReferenceHolder<T> other && Equals(other); } public bool Equals(ReferenceHolder<T> other) { var x = TryGetTarget(); var y = other.TryGetTarget(); if (x is null) { if (_weakReference is object) { // 'x' is a weak reference that was collected. Verify 'y' is a collected weak reference with the // same runtime hash code. This code path can fail in an edge case where the references to two // different objects have both been collected, but the runtime hash codes for the objects were // equal. Callers can ensure this case is not encountered by structuring equality checks such that // at least one of the objects is alive at the time Equals is called. return y is null && other._weakReference is object && _hashCode == other._hashCode; } else { // Null values are equal iff both were originally references to null. return y is null && other._weakReference is null; } } // Intentional reference equality check return x == y; } public override int GetHashCode() { if (_weakReference is object) return _hashCode; return ReferenceEqualityComparer.Instance.GetHashCode(_strongReference); } internal static class TestAccessor { /// <summary> /// Creates a <see cref="ReferenceHolder{T}"/> for a weakly-held reference that has since been collected. /// </summary> /// <param name="hashCode">The hash code of the collected value.</param> /// <returns>A weak <see cref="ReferenceHolder{T}"/> which was already collected.</returns> public static ReferenceHolder<T> ReleasedWeak(int hashCode) => new(new WeakReference<T>(null!), hashCode); } }